/*
This file is part of Docking@Home.
http://docking.gcl.cis.udel.edu
Copyright (C) 2009 University of Delaware

Docking@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Docking@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Docking@Home.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
	Filename: GLHelper.cpp
	Description: A public class of OpenGL functions that are commonly used
		in this application so that one does not have to rewrite them at any
		given point.
	Revision History: 
		Created - 01/02/08
	Author: Robert Keller
*/

#ifdef __LINUX
# include "intel_compatibility.h"
#endif

#include "GLHelper.h"
#include "BioModels/ColorEnum.h"

#include "gutil.h"
#include "boinc_gl.h"
#include "boinc_api.h"
#include "graphics2.h"
#include "txf_util.h"
#include "filesys.h"
#include "mfile.h"
#include "parse.h"
#include "util.h"
#include "app_ipc.h"
#include "diagnostics.h"

#ifdef _WIN32

#else

#define _MAX_PATH 256

#endif
// ------------------------------------------------------------
// Private Static Member Declaration
// ------------------------------------------------------------

TexFont* GLHelper::texFonts [TXF_NUM_FONT];
const char * GLHelper::DEFAULT_TEXFONT_PATH = ".";

// ------------------------------------------------------------
// Static Color Constants
// ------------------------------------------------------------

const float GLHelper::WHITE[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
const float GLHelper::CYAN[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
const float GLHelper::BLUE[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
const float GLHelper::RED[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
const float GLHelper::YELLOW[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
const float GLHelper::ORANGE[4] = { 1.0f, 0.5f, 0.0f, 1.0f };
const float GLHelper::GRAY[4] = { 0.35f, 0.35f, 0.35f, 1.0f };
const float GLHelper::TAN[4] = { 0.86f, 0.58f, 0.44f, 1.0f }; 
const float GLHelper::SILVER[4] = { 0.60f, 0.60f, 0.60f, 1.0f }; 
const float GLHelper::BLACK[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

// ------------------------------------------------------------
// Static Text Shifting Constants
// ------------------------------------------------------------

const float GLHelper::AVERAGE_TEXT_SHIFT_RIGHT = 8.0f;
const float GLHelper::AVERAGE_TEXT_SHIFT_UP = 1.0f;

// ------------------------------------------------------------
// Constructor / Destructor
// ------------------------------------------------------------

GLHelper::GLHelper ()
{
}

GLHelper::~GLHelper ()
{
}// ~GLHelper

// ------------------------------------------------------------
// Render Mode Functions (Orthographic, Perspective)
// ------------------------------------------------------------

void GLHelper::setOrthographic () 
{
    int viewport[4];
    get_viewport(viewport);
	
	// Disable GL Properties

	glDisable(GL_CULL_FACE);
	glDisable(GL_DEPTH_TEST);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    glOrtho(0.0, viewport[2], viewport[3], 0.0, 0.0, 200.0);
    
	glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    
	gluLookAt(
        0.0, 0.0, 200.0,        // eye position
        0, 0, 0,              // where we're looking
        0.0, 1.0, 0.0          // up is in positive Y direction
    );

}// setOrthographic

void GLHelper::setOrthographicDone() 
{
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

	// Enable GL Properties

	glFrontFace(GL_CCW);
	glDisable(GL_CULL_FACE);
	glDisable(GL_DEPTH_TEST);

}// setOrthographicDone

// ------------------------------------------------------------
// Viewport Functions
// ------------------------------------------------------------

// Name: setViewport
// Description: Sets the current OpenGL viewport (area of the 
//		screen being rendered to).
// Parameters: x, the bottom left x
//			   y, the bottom left y
//             x2, the top right x
//             y2, the top right y
//	- Note these are not screen coordinates !!!
//		- Screen coordinates are 0,0 at top left and w,h at 
//			bottom right.
//		- OpenGL viewport coordinates are 0,0 at bottom left and 
//			w,h at top right.

void GLHelper::setViewport(int x, int y, int x2, int y2)
{
	int w = x2 - x;
	int h = y2 - y;

	glViewport(x, y, w, h);

}// setInnerViewport

void GLHelper::setViewportProperties (double dist)
{
	double x, y, z;
    
	x = 0.0;
    y = 0.0;
    z = 11.0*dist;

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
	gluLookAt(
        x, y, z,        // eye position
        0,0,0,        // where we're looking
        0.0, 1.0, 0.0    // up is in positive Y direction
    );

}// setViewportProperties

int GLHelper::getViewportWidth ()
{
	int view[4];
	
	glMatrixMode(GL_MODELVIEW);
	glGetIntegerv(GL_VIEWPORT, view);

	return view[2];
}// getViewportWidth

int GLHelper::getViewportHeight ()
{
	int view[4];
	
	glMatrixMode(GL_MODELVIEW);
	glGetIntegerv(GL_VIEWPORT, view);

	return view[3];
}// getViewportHeight

// ------------------------------------------------------------
// Camera Functions
// ------------------------------------------------------------

void GLHelper::initCamera (double fov, double aspectRatio,
	double nearClip, double farClip)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    
	gluPerspective(
        fov,			// field of view in degree
        aspectRatio,    // aspect ratio
        nearClip,       // Z near clip
        farClip			// Z far clip
    );
}// initCamera

// ------------------------------------------------------------
// Lighting Functions
// ------------------------------------------------------------

void GLHelper::initLights(float ambient[4], float position[4], float direction[4]) 
{
   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
   glLightfv(GL_LIGHT0, GL_POSITION, position);
   glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
}// initLights

// ------------------------------------------------------------
// Rectangle Drawing Functions
// ------------------------------------------------------------

void GLHelper::drawRect (float x1, float y1, float x2, 
	float y2, const float color[4])
{
	glColor4fv(color);

	glBegin(GL_LINE_STRIP);

	glVertex2f(x1, y1);
	glVertex2f(x2, y1);
	glVertex2f(x2, y2);
	glVertex2f(x1, y2);
	glVertex2f(x1, y1);
	
	glEnd();
}// drawRect

void GLHelper::drawRectWH (float x1, float y1, float width, 
	float height, const float color[4])
{
	glColor4fv(color);

	glBegin(GL_LINE_STRIP);

	glVertex2f(x1, y1);
	glVertex2f(x1 + width, y1);
	glVertex2f(x1 + width, y1 + height);
	glVertex2f(x1, y1 + height);
	glVertex2f(x1, y1);
	
	glEnd();
}// drawRect

void GLHelper::drawFilledRectWH (float x1, float y1, float width, 
	float height, const float color[4])
{
	glColor4fv(color);
	glRectf(x1,y1+height,x1+width,y1);
}// drawFilledRect

// ------------------------------------------------------------
// Line Drawing Functions
// ------------------------------------------------------------

void GLHelper::drawLine (float pos1[3], float pos2[3], 
	const float color[4])
{
	glBegin(GL_LINES);
	
	glColor4f(color[0], color[1], color[2], color[3]);
	glVertex3f(pos1[0], pos1[1], pos1[2]);
	glVertex3f(pos2[0], pos2[1], pos2[2]);	

	glEnd();
}// drawLine

void GLHelper::drawLine (float x1, float y1, 
	float x2, float y2, const float color[4])
{
	glBegin(GL_LINES);
	
	glColor4f(color[0], color[1], color[2], color[3]);
	glVertex2f(x1, y1);
	glVertex2f(x2, y2);	

	glEnd();
}// drawLine

void GLHelper::drawLine (float x1, float y1, float z1, 
	float x2, float y2, float z2, const float color[4])
{
	glBegin(GL_LINES);
	
	glColor4f(color[0], color[1], color[2], color[3]);
	glVertex3f(x1, y1, z1);
	glVertex3f(x2, y2, z2);	

	glEnd();
}// drawLine

// ------------------------------------------------------------
// Quadrilateral Drawing Functions
// ------------------------------------------------------------

void GLHelper::drawQuad (float x1, float y1, float x2,
	float y2, const float color[4])
{
	glColor4fv(color);

	glBegin(GL_QUADS);
		
	glVertex3f(x1, y1, 0.0f);
	glVertex3f(x1, y2, 0.0f);		
	glVertex3f(x2, y2, 0.0f);
	glVertex3f(x2, y1, 0.0f);
	
	glEnd();
}// drawQuad

void GLHelper::drawQuadWH (float x1, float y1, float width,
	float height, const float color[4])
{
	glColor4fv(color);

	glBegin(GL_QUADS);
		
	glVertex3f(x1, y1, 0.0f);
	glVertex3f(x1, y1 + height, 0.0f);		
	glVertex3f(x1 + width, y1 + height, 0.0f);
	glVertex3f(x1 + width, y1, 0.0f);
	
	glEnd();
}// drawQuad

void GLHelper::drawTexturedQuad (float x1, float y1, float x2,
	float y2, const float color[4], unsigned int texID)
{

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texID);

	glColor4fv(color);

	glBegin(GL_QUADS);
	
	glTexCoord2f (0.0f, 0.0f);
	glVertex3f(x1, y1, 0.0f);

	glTexCoord2f (0.0f, 1.0f);
	glVertex3f(x1, y2, 0.0f);

	glTexCoord2f (1.0f, 1.0f);
	glVertex3f(x2, y2, 0.0f);

	glTexCoord2f (1.0f, 0.0f);
	glVertex3f(x2, y1, 0.0f);
	
	glEnd();

	glDisable(GL_TEXTURE_2D);

}// drawTexturedQuad

void GLHelper::drawTexturedQuadWH (float x1, float y1, float width,
	float height, const float color[4], unsigned int texID)
{

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texID);

	glColor4fv(color);

	glBegin(GL_QUADS);
	
	glTexCoord2f (0.0f, 0.0f);
	glVertex3f(x1, y1, 0.0f);

	glTexCoord2f (0.0f, 1.0f);
	glVertex3f(x1, y1 + height, 0.0f);		

	glTexCoord2f (1.0f, 1.0f);
	glVertex3f(x1 + width, y1 + height, 0.0f);

	glTexCoord2f (1.0f, 0.0f);
	glVertex3f(x1 + width, y1, 0.0f);
	
	glEnd();

	glDisable(GL_TEXTURE_2D);

}// drawTexturedQuadWH

// ------------------------------------------------------------
// Cylinder Drawing Functions
// ------------------------------------------------------------

void GLHelper::drawCylinder(float x, float y, float z, float len, 
	float radius, int angle, float rotAxis[3]) 
{
    GLUquadricObj* quadric = gluNewQuadric();

    glPushMatrix();
    
	glTranslatef(x, y, z);
	glRotated(angle, rotAxis[0], rotAxis[1], rotAxis[2]);
    
	gluCylinder(quadric, radius, radius, len, 20, 20);
    
	gluDeleteQuadric(quadric);
    
	glPopMatrix();
}// drawCylinder

void GLHelper::drawCylinder(float pos[3], float len, 
	float radius, int angle, float rotAxis[3]) 
{
    GLUquadricObj* quadric = gluNewQuadric();

    glPushMatrix();
    
	glTranslatef(pos[0], pos[1], pos[2]);
	glRotated(angle, rotAxis[0], rotAxis[1], rotAxis[2]);
    
	gluCylinder(quadric, radius, radius, len, 20, 20);
    
	gluDeleteQuadric(quadric);
    
	glPopMatrix();
}// drawCylinder

// ------------------------------------------------------------
// Graph Drawing Functions
// ------------------------------------------------------------

/* Name: drawAxis
   Description: Draws a graph's axis with the origin located
	at x,y if there are no tick marks otherwise it is adjusted 
	to maintain the lower left corner of the graph is still at 
	x,y. The axis' have a size of width and height and if 
	ticks are used, it will return the new x,y values as well
	as the adjust graph width and height and adjusted mins and 
	maxs to integers
*/
void GLHelper::drawAxis(float &x, float &y, float &width, 
		float &height, float &xMin, float &yMin, float &xMax, 
		float &yMax, float textXScale, float textYScale, 
		const float axisColor[4], bool bTicks)
{
	const float textPadding = 5.0f;
	float xPartitions;
	float yPartitions;
	
	float xTickSize;
	float yTickSize;
	
	char axisStr[5];

	int length;
	int textWidth;
	int textHeight;
	int max_ascent;
	int max_descent;
	float color[4];

	if (bTicks)
	{
		copyColor4f(color, axisColor);

		// Set tick size	
		xTickSize = height / 15.0f;
		yTickSize = width / 15.0f;
		
		// Makes room for tick marks
		width = width - yTickSize;
		height = height - xTickSize;

		xPartitions = width / 5.0f;
		yPartitions = height / 5.0f;

		x += yTickSize;
		y -= xTickSize;
		
		// Set Mins and Maxs to integer values for the scales
		/*(xMin < 0) ? xMin = floor(xMin) : xMin = ceil(xMin);
		(yMin < 0) ? yMin = floor(yMin) : yMin = ceil(yMin);
		(xMax < 0) ? xMax = floor(xMax) : xMax = ceil(xMax);
		(yMax < 0) ? yMax = floor(yMax) : yMax = ceil(yMax);*/
		if(xMax - xMin < 0){
			xMin =(float)((int)(xMin+.5));
			xMax =(float)((int)(xMax-.5));
		}
		else{
			xMin =(float)((int)(xMin-.5));
			xMax =(float)((int)(xMax+.5));
		}
		if(yMax - yMin < 0){
			yMin =(float)((int)(yMin+.5));
			yMax =(float)((int)(yMax-.5));
		}
		else{
			yMin =(float)((int)(yMin-.5));
			yMax =(float)((int)(yMax+.5));
		}

		// Y Axis Vlaues
		length = sprintf (axisStr, "%.0f", yMin);
		getDrawnTextProperties(axisStr, 0, &textWidth, 
			&max_ascent, &max_descent);
		textWidth += (int)(AVERAGE_TEXT_SHIFT_RIGHT + textPadding);
		textHeight = max_ascent + max_descent;
		drawText(0.1f, 
			x - textWidth * textXScale - yTickSize, 
			y - textHeight * textYScale * 0.5f, 
			1.0, 
			textXScale, textYScale, 1.0, 
			color, 0, axisStr);

		length = sprintf (axisStr, "%.0f", yMax);
		getDrawnTextProperties(axisStr, 0, &textWidth, 
			&max_ascent, &max_descent);
		textWidth += (int)(AVERAGE_TEXT_SHIFT_RIGHT + textPadding);
		textHeight = max_ascent + max_descent;
		drawText(0.1f,
			x - textWidth * textXScale - yTickSize, 
			y - height - textHeight * textYScale * 0.5f, 
			1.0, 
			textXScale, textYScale, 1.0, 
			color, 0, axisStr);

		// X Axis Values
		length = sprintf (axisStr, "%.0f", xMin);
		getDrawnTextProperties(axisStr, 0, &textWidth, 
			&max_ascent, &max_descent);
		textWidth += (int)AVERAGE_TEXT_SHIFT_RIGHT;
		textHeight = max_ascent + max_descent + (int)textPadding;
		drawText(0.1f,
			x - textWidth * textXScale * 0.7, 
			y - textHeight * textYScale + xTickSize, 
			1.0, 
			textXScale, textYScale, 1.0, 
			color, 0, axisStr);

		length = sprintf (axisStr, "%.0f", xMax);
		getDrawnTextProperties(axisStr, 0, &textWidth, 
			&max_ascent, &max_descent);
		textWidth += (int)AVERAGE_TEXT_SHIFT_RIGHT;
		textHeight = max_ascent + max_descent + (int)textPadding;
		drawText(0.1f,
			x + width - textWidth * textXScale * 0.7, 
			y - textHeight * textYScale + xTickSize, 
			1.0, 
			textXScale, textYScale, 1.0, 
			color, 0, axisStr);

		// Draw Y Ticks
		for (int i = 0; i <= 5; i ++)
		{
			drawLine(x, 
				y - i * yPartitions, 
				1.0f, 
				x - yTickSize,
				y - i * yPartitions, 
				1.0f, 
				axisColor);
		}// for

		// Draw X Ticks
		for (int i = 0; i <= 5; i ++)
		{
			drawLine(x + i * xPartitions, 
				y, 
				x + i * xPartitions, 
				y + xTickSize, 
				axisColor);
		}// for
	}// if

	// Draw Axis'
	drawLine(x, y, x, y - height, axisColor);
	drawLine(x, y, x + width, y, axisColor);
}

/* Name: drawLineGraph
   Description: Draws a line graph that has a lower left
	corner of x,y and has the specified width and height.
*/
void GLHelper::drawLineGraph (float x, float y, float width, 
		float height, float xMin, float yMin, float xMax, 
		float yMax, float textXScale, float textYScale, 
		vector<Point2fT> points, 
		const float axisColor[4], bool bTicks)
{
	float xRatio;
	float yRatio;

	drawAxis(x, y, width, height, xMin, yMin, 
		xMax, yMax, textXScale, textYScale, 
		axisColor, bTicks);

	if (points.size() > 1)
	{
		xRatio = width / (xMax - xMin);		
		yRatio = height / (yMax - yMin);

		for (unsigned int i = 1; i < points.size(); i++)
		{
			drawLine((points[i-1].s.X - xMin) * xRatio + x, 
				y - (points[i-1].s.Y - yMin) * yRatio, 
				(points[i].s.X - xMin)* xRatio + x, 
				y - (points[i].s.Y - yMin) * yRatio,
				axisColor);
		}// for
	}// if
	
}// drawLineGraph

// ------------------------------------------------------------
// Text Functions
// ------------------------------------------------------------

bool GLHelper::fontsLoaded ()
{
	if (texFonts[0] == NULL)
		return false;
	else
		return true;

}// fontsLoaded

void GLHelper::loadFonts(const char * dir)
{
    char vpath[_MAX_PATH], phys_path[_MAX_PATH];

    for (int i=0 ; i<TXF_NUM_FONT; i++){
	    sprintf(vpath, "%s/%s", dir, font_names[i]);
	    boinc_resolve_filename(vpath, phys_path, sizeof(phys_path));
		//KEV
		//to debug correctly set project settings->debug->working directory to
		//a dir with Helvetica.txf, logo.jpg etc there so the exe can find them
		//remember that unix is case sensitive!
	    if (is_file(phys_path)) {
		    texFonts[i] = txfLoadFont(phys_path);
		    if(texFonts[i]) 
		    {		
			    CreateTexFont(texFonts[i], 0, GL_TRUE);
		    }
	    }
		else{
			//KEV
			printf("Sorry can't load %s\n",vpath);
		}
    }	
}// loadFonts

void GLHelper::loadDefaultFonts()
{
	loadFonts(DEFAULT_TEXFONT_PATH);
}// loadDefaultFonts

void GLHelper::drawText(float alphaValue, double x, double y, 
		double z, float xscale, float yscale, float zscale,
		const float color[4], int fontIndex, char * str)
{
	glPushMatrix();
	glEnable(GL_TEXTURE_2D);
	if((fontIndex < TXF_NUM_FONT) && texFonts[fontIndex]) {
		glBindTexture(GL_TEXTURE_2D, texFonts[fontIndex]->texobj);
		glTranslated(x, y, z);
		glScalef(xscale, yscale, zscale);
		glEnable(GL_ALPHA_TEST);
		// use .1 and .5 for a dark and bright background respectively
		glAlphaFunc(GL_GEQUAL, alphaValue);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glColor4fv(color);
		txfRenderString(texFonts[fontIndex], str, (int)strlen(str));
	}
	glDisable(GL_TEXTURE_2D);	
	glPopMatrix();
}// drawText

void GLHelper::getDrawnTextProperties (char * str, int fontIndex,
		int * width, int * max_ascent, int * max_descent)
{
	if (!fontsLoaded())
		loadDefaultFonts();

	txfGetStringMetrics(texFonts[fontIndex], str, (int)strlen(str), 
			width, max_ascent, max_descent);

}// getDrawnTextProperties

// ------------------------------------------------------------
// Color Functions
// ------------------------------------------------------------

void GLHelper::getColorFromID (float color[4], int id)
{
	switch (id)
	{
		case WHITE_ID:
			copyColor4f(color, WHITE);
			break;
		case CYAN_ID:
			copyColor4f(color, CYAN);
			break;
		case BLUE_ID:
			copyColor4f(color, BLUE);
			break;
		case RED_ID:
			copyColor4f(color, RED);
			break;
		case YELLOW_ID:
			copyColor4f(color, YELLOW);
			break;
		case ORANGE_ID:
			copyColor4f(color, ORANGE);
			break;
		case GRAY_ID:
			copyColor4f(color, GRAY);
			break;
		case TAN_ID:
			copyColor4f(color, TAN);
			break;
		case SILVER_ID:
			copyColor4f(color, SILVER);
			break;
		case BLACK_ID:
			copyColor4f(color, BLACK);
			break;
		default:
			copyColor4f(color, WHITE);
	}// switch
}// getColorFromID

// ------------------------------------------------------------
// Color Copy Functions
// ------------------------------------------------------------

void GLHelper::copyColor4f (float dest[4], const float src[4])
{
	memcpy(dest, src, 4*sizeof(float));
}// copyColor4f

// ------------------------------------------------------------
// Cleanup
// ------------------------------------------------------------

void GLHelper::cleanup()
{
	for (int i = 0; i < TXF_NUM_FONT; i++)
	{
		if (texFonts[i] != NULL)
		{
			txfUnloadFont(texFonts[i]);
			texFonts[i] = NULL;
		}//if 
	}// for
}// cleanup
